<?php 
ArrowController::importModel("org.arrowplatform.orm.ArrowOrmCriteria");


class ArrowOrmJoinCriteria extends ArrowOrmCriteria implements ArrayAccess,IteratorAggregate,Countable{
	
	const JOIN_TYPE_LEFT = "LEFT";
	const JOIN_TYPE_RIGHT = "RIGHT";
	const JOIN_TYPE_INNER = "INNER";
	
	private $criteriaSet = array();
	private $baseCriteria = null;
	private $baseClass = null;	
	private $joinType = self::JOIN_TYPE_LEFT;
	private $ignoredRelations = array();
	private $joinOnConditions = array();
	private $searchConditions = array();
	private $virtualLinks = array() ;

	private $orderPriority = 0;
	
	public function __construct( $criteriaSet ){
		foreach ($criteriaSet as $key=>&$criteria){
			if (is_string($criteria))
				$criteria = new ArrowOrmCriteria($criteria);
			$class = explode('.',$criteria->getModel());
			$class = end($class);
			$this->criteriaSet[$class] = $criteriaSet[$key];
			if ($this->baseCriteria == null){ 
				$this->baseCriteria = $this->criteriaSet[$class];
				$this->baseClass = $class;
			}	
		}
	}
	
	public function addCriteria( $criteria ) {
		$class = explode('.',$criteria->getModel());
		$class = end($class);
		if( isset( $this->criteriaSet[$class] ) ) throw new ArrowException("[ArrowOrmJoinCriteria::addCriteria] Criteria exist.");
		$this->criteriaSet[$class] = $criteria ;
	} 
	
	public function getCriteriaSet(){
		return $this->criteriaSet;
	}
	
	public function offsetExists ($offset){
		return isset( $this->criteriaSet[$offset] );
	}
 	public function offsetGet ($offset){
 		return $this->criteriaSet[$offset];
 	}
 	public function offsetSet ($offset, $value){
 		$this->criteriaSet[$offset] = $value;
 	}
 	public function offsetUnset ($offset){
 		unset($this->criteriaSet[$offset]);
 	}
 	
	public function getIterator(){
		return new ArrayIterator($this->criteriaSet);
	}
	
	public function count(){
		return count($this->criteriaSet);
	}

	public function setEmptyList(){
		foreach($this->criteriaSet as $key => $criteria){
			$criteria->setEmptyList();
		}
	}

	public function setJoinType($joinType){
		$this->joinType = $joinType;		
	}

	
	public function getJoinType(){
		return $this->joinType;		
	}
		
	public function setLimit($offset, $lenght){
		$this->baseCriteria->setLimit($offset, $lenght);
	}
	
	public function clearLimit(){
		$this->baseCriteria->clearLimit();
	}
	
	/**
	 * Return list of connections between classes declared in this joinCriteria
	 * 
	 * @return unknown_type
	 */
	public function getAvailableRelations(){
		$classes = array();
		foreach ($this->criteriaSet as $obj ){
			$add = explode('.',$obj->getModel());
			$classes[] = end($add);
		}
		$related = array();			
		$allConf = ArrowOrmConfig::getClassConf();

		foreach ($classes as $cls){
			//getRid of internal Class data
			$cls = explode('[',$cls);
			$cls = $cls[0];
			foreach ($allConf as $classConf){
				$relClass = $classConf['attr']['class'];
				if (isset($classConf['key']['foreign'][$cls]) && in_array($relClass,$classes) ){
					$related[$cls][$relClass] = $relClass;
					$related[$relClass][$cls] = $cls;
				}	
			}
		}
		
		// add virtual relations
		foreach( $this->virtualLinks as $kvl => $vvl ) {
			foreach( $vvl as $ksl => $vsl ) {
				$related[ $kvl ][ $ksl ] = $ksl ;
				$related[ $ksl ][ $kvl ] = $kvl ;	
			}
		}
		
		foreach ($this->ignoredRelations as $rel){
			if ( isset($related[$rel['base']][$rel['rel']]) )
				unset($related[$rel['base']][$rel['rel']]);
			if ( isset($related[$rel['rel']][$rel['base']]) )
				unset($related[$rel['rel']][$rel['base']]);
		}
		
		return $related;
	}
	
	public function ignoreRelation($baseClass,$foreignClass){
		$this->ignoredRelations[] = array('base'=>$baseClass,'rel'=>$foreignClass);	
	}

	public function getBaseClass(){
		return $this->baseClass;
	}
	
	private function decodeColumn($col){
		$pattern = '/(.*?)(\[(.*)\])?:(.*)/';
		preg_match($pattern, $col, $matches);
		if ( !empty($matches[3]) ){
			if( strpos($matches[3],'::')===false){
				$mainClass = $this->baseClass;
				$matches[3] = '['.$mainClass.'::'.$matches[3].']';
			}	
			else
				$matches[3] = '['.$matches[3].']';
		}
		
		if (empty($matches)){
			//column from base class
			return array($col);
		}
		$data = array($matches[1].$matches[3],$matches[4]);
		return $data;
	}
	
	public function addColumn($column, $alias = "", $aggregate = ""){
		$column = $this->decodeColumn($column);
		if(count($column) == 1){
			$this->baseCriteria->addColumn($column[0],$alias, $aggregate);
		}else{
			$this->findCriteria($column[0])->addColumn($column[1],$alias, $aggregate);
		}
		if ($aggregate !==""){
			foreach($this->criteriaSet as $criteria)
				$criteria->aggregates = true;
			$this->aggregates = true;
		}
	}
	
	public function addCondition($column, $value, $condition = self::C_EQUAL){
		$column = $this->decodeColumn($column);
		if(count($column) == 1){
			$this->baseCriteria->addCondition($column[0], $value, $condition);
		}else{
			$this->findCriteria($column[0])->addCondition($column[1], $value, $condition);
		}
	}
	
	public function addSearchCondition($columns, $value, $condition = self::C_LIKE){
		if (!is_array($columns))
		$columns = array($columns);
		$this->searchConditions[] = array('columns'=>$columns,'condition'=>$condition,'value'=>$value);
	}
	
	public function getSearchConditions(){
		return $this->searchConditions;
	}
	
	public function addGroupBy( $column ){
		$column = $this->decodeColumn($column);
		if(count($column) == 1){
			$this->baseCriteria->addGroupBy($column[0] );
		}else{
			$this->findCriteria($column[0])->addGroupBy($column[1] );
		}
	}
	
	public function addOrderBy( $column, $orderType=self::O_ASC, $order_priority =''){
		//order priority jest tylko do użytku wewnętrzenego 
		$column = $this->decodeColumn($column);
		$priority = $this->orderPriority+1;
		$this->orderPriority = $priority; 
		if(count($column) == 1){
			$this->baseCriteria->addOrderBy($column[0], $orderType, $priority );
		}else{
			$this->findCriteria($column[0])->addOrderBy($column[1], $orderType, $priority );
		}
	}
	public function removeOrder(){
		foreach($this->criteriaSet as $criteria){
			$criteria->removeOrder();
		}
	}
	
	public function removeGroup(){
		foreach($this->criteriaSet as $criteria){
			$criteria->removeGroup();
		}
	}
	
	public function startGroup($group){
		$this->baseCriteria->startGroup($group);
	}
	
	public function endGroup(){
		$this->baseCriteria->endGroup();
	}
	
	public function setDistinct(){
		$this->baseCriteria->setDistinct();
	}
	
	public function findCriteria($class){
			if (strpos($class,'[') !== false){
				//there is inner class in this name
				$baseClass =  substr($class, 0, strpos($class,'[') );
				$innerClass = substr($class, strpos($class,'[')+1, -1);
			}
		
			if( isset($this->criteriaSet[$class]))
				return $this->criteriaSet[$class];
			else{
				//generate additional criteria (for multi joins only)
				if (isset($baseClass) && isset($this->criteriaSet[$baseClass]) ){
					$this->criteriaSet[$class] = clone $this->criteriaSet[$baseClass];
					return $this->criteriaSet[$class];		
				}
			}
			throw new ArrowCoreException(array("[ArrowOrmJoinCriteria] Criteria with class '{$class}' not found"));
	}
	
	public function getModel(){
		return $this->baseCriteria->getModel();
	}
	
	public function __clone(){
		$this->baseCriteria = false;
		foreach ($this->criteriaSet as $key=>$value){
			$this->criteriaSet[$key] = clone $value;
			if ($this->baseCriteria === false)
				$this->baseCriteria = $this->criteriaSet[$key];
		}
	}
	
 	public function addJoinOnCondition( $base_class, $join_class, $condition ){
 		$this->joinOnConditions[$base_class][$join_class][] = $condition ;
 		$this->joinOnConditions[$join_class][$base_class][] = $condition ;
	}
	
	public function getJoinOnConditions( $base_class, $join_class ) {
		if( isset( $this->joinOnConditions[$base_class][$join_class] ) )
			return $this->joinOnConditions[$base_class][$join_class] ;
		return array();
	}
	
 public function removeCondition($column,$value=null,$condition_type=null){
 	return $this->baseCriteria->removeCondition($column,$value,$condition_type);
 }

 /*checks whether condition exists in criteria 
  * set $value or $condiiton_type to null to ignore it
  * 
  */  
 public function conditionExists($column,$value=null,$condition_type=null){
	return $this->baseCriteria->conditionExists($column,$value,$condition_type);
 }

 public function addCustomCondition($str, $tables = array() ){
	return $this->baseCriteria->addCustomCondition($str, $tables );
 }
 
 public function isGroupBy() {
 	return $this->baseCriteria->isGroupBy();
 }
 
 public function addVirtualRelation( $base_class, $base_field, $join_class, $join_field ) {
 	$this->virtualLinks[ $base_class ][ $join_class ][] = array( "base_field" => $base_field, "join_field" => $join_field ) ;
 }
 
 public function getVirtualRelations( ) { return $this->virtualLinks ; }
 
}
?>